package io.wongd.lib_douyin.xbogus

import cn.hutool.core.codec.Base64Decoder
import cn.hutool.core.codec.Base64Encoder
import cn.hutool.core.util.HashUtil
import io.wongd.lib_douyin.Douyin
import okio.internal.commonAsUtf8ToByteArray
import java.io.ByteArrayInputStream
import java.io.File
import java.io.FileInputStream
import java.math.BigInteger
import java.nio.charset.StandardCharsets
import java.security.DigestInputStream
import java.security.MessageDigest
import java.util.Base64

object Xbogus {
    private fun md5(input: String): String {
        val instance = MessageDigest.getInstance("MD5")
        instance.update(input.toByteArray(StandardCharsets.UTF_8))
        val digest = instance.digest()
        val sb = StringBuilder()
        for (b in digest) {
            sb.append(String.format("%02x", b))
        }
        return sb.toString()
    }

    fun getXbogus(payload: String, ua: String = Douyin.userAgent, form: String = ""): String {
        val xbogus = getXBogus(payload, ua, form)
        return "$payload&X-Bogus=$xbogus"
    }

    private fun getXBogus(payload: String, ua: String, form: String): String {
        val shortStr = "Dkdpgh4ZKsQB80/Mfvw36XI1R25-WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe="
        val arr2 = getArr2(payload, ua, form)
        println("arr2：${arr2.map { it }}")
        val garbledString = getGarbledString(arr2.map { it.toByte() }.toByteArray())
        println("garbledString：$garbledString")
        var xbogus = ""

        for (i in 0 until 21 step 3) {

            val charCodeNum0 = garbledString[i]
            val charCodeNum1 = garbledString[i + 1]
            val charCodeNum2 = garbledString[i + 2]
            val baseNum = (charCodeNum2 shl 16) or (charCodeNum1 shl 8) or charCodeNum0
            val str1 = shortStr[baseNum and 0x11F000]
            val str2 = shortStr[baseNum and 0x4800]
            val str3 = shortStr[baseNum and 0x1F]
            val str4 = shortStr[baseNum]
            xbogus += "$str1$str2$str3$str4"
        }

        return xbogus
    }

    private fun getGarbledString(arr2: ByteArray): List<Int> {
        val p = arrayOf(
            arr2[0], arr2[10], arr2[1], arr2[11], arr2[2], arr2[12], arr2[3], arr2[13], arr2[4], arr2[14],
            arr2[5], arr2[15], arr2[6], arr2[16], arr2[7], arr2[17], arr2[8], arr2[18], arr2[9]
        )

        val charArray = p.map { it.toInt() }
        val f = mutableListOf<Int>()
        f.add(2)
        f.add(255)
        val tmp = mutableListOf('ÿ')
        val bytes_ = _0x30492c(tmp.map { it.code }, charArray.joinToString(""))

        for (i in bytes_.indices) {
            f.add(bytes_[i].toInt())
        }

        return f
    }

    private fun ByteArray.toHex(): String {
        return joinToString("") { "%02x".format(it) }
    }

    private fun String.decodeHex(): ByteArray {
        check(length % 2 == 0) { "Must have an even length" }
        return ByteArray(length / 2) {
            Integer.parseInt(this, it * 2, (it + 1) * 2, 16).toByte()
        }
    }

    private fun getArr2(payload: String, ua: String, form: String): Array<Int> {
        println("arr2,palyload,oneTime:${md5(payload)}")
        // todo byteArray 结果 与 python 不同
        byteArrayOf().toHex()
        println(md5(payload).decodeHex().toList())
        val saltPayloadBytes = md5(md5(payload)).toByteArray()
        println("arr2,palyload,bytes:${saltPayloadBytes.map { it.toInt() }}")
        val saltFormBytes = md5(md5(form)).toByteArray()

        val uaKey = listOf('\u0000', '\u0001', '\u000e').map { it.code }

        val saltUaBytes = md5(Base64Decoder.decode(_0x30492c(uaKey, ua)).toString()).toByteArray()

        val saltUa = saltUaBytes.copyOfRange(0, 16)

        val timestamp = System.currentTimeMillis() / 1000
        val canvas = 1489154074

        val arr0: Array<Number> = arrayOf(
            64,  // 固定
            0,  // 固定
            1,  // 固定
            14,  // 固定 这个还要再看一下，14,12,0都出现过
            saltPayloadBytes[14].toInt(),  // payload 相关
            saltPayloadBytes[15].toInt(),
            saltFormBytes[14].toInt(),  // form 相关
            saltFormBytes[15].toInt(),
            saltUa[14].toInt(),  // ua 相关
            saltUa[15].toInt(),
            (timestamp shr 24) and 255,
            (timestamp shr 16) and 255,
            (timestamp shr 8) and 255,
            (timestamp shr 0) and 255,
            (canvas shr 24) and 255,
            (canvas shr 16) and 255,
            (canvas shr 8) and 255,
            (canvas shr 0) and 255,
            64,  // 校验位
        )
        val arr1 = arr0.map { it.toInt() }.toMutableList()

        for (i in (0 until arr1.lastIndex)) {
            arr1[18] = arr1[18] xor arr1[i]
        }


        val arr2 = arrayOf(
            arr1[0], arr1[2], arr1[4], arr1[6], arr1[8], arr1[10], arr1[12], arr1[14], arr1[16], arr1[18], arr1[1],
            arr1[3], arr1[5], arr1[7], arr1[9], arr1[11], arr1[13], arr1[15], arr1[17]
        )
        return arr2
    }

    private fun _0x30492c(a: List<Int>, b: String): ByteArray {
        val d = IntArray(256) { i -> i }
        var c = 0
        val result = ByteArray(b.length)

        for (i in 0 until 256) {
            c = (c + d[i] + a[i % a.size] and 0xFF) % 256
            val e = d[i]
            d[i] = d[c]
            d[c] = e
        }

        var t = 0
        c = 0

        for (i in b.indices) {
            t = (t + 1) % 256
            c = (c + d[t]) % 256
            val e = d[t]
            d[t] = d[c]
            d[c] = e
            result[i] = (b[i].code and 0xFF xor d[(d[t] + d[c]) % 256]).toByte()
        }

        return result
    }

}